package state

type LuaStack struct {
	slots   []luaValue
	top     int //这里的top 在go 里面指向 栈顶+1 ,在 lua 栈里面指向 栈顶(Lua栈从1开始的)
	state   *luaState
	prev    *LuaStack
	closure *closure
	varargs []luaValue
	Pc      int
	openuvs map[int]*upvalue //记录还处于开放状态的 upvalues
}

func NewLuaStack(n int, state *luaState) *LuaStack {
	return &LuaStack{
		slots: make([]luaValue, n),
		top:   0,
		state: state,
	}
}

func (this *LuaStack) check(n int) {
	free := len(this.slots) - this.top
	if free <= n {
		//扩容
		addLen := len(this.slots)
		if addLen >= 2048 {
			addLen = addLen >> 1
		}
		this.slots = append(this.slots, make([]luaValue, addLen)...)
	}
}

func (this *LuaStack) push(v luaValue) {
	this.check(this.top + 1)
	this.slots[this.top] = v
	this.top++
}

func (this *LuaStack) pop() luaValue {
	if this.top < 1 {
		panic("pop stack overflow")
	}
	this.top--
	return this.slots[this.top]
}

func (this *LuaStack) absIndex(idx int) int {
	if idx <= LUA_REGISTRYINDEX {
		return idx
	}
	if idx >= 0 {
		return idx
	}
	return idx + this.top + 1
}
func (this *LuaStack) isValid(idx int) bool {
	if idx < LUA_REGISTRYINDEX {
		uvIdx := LUA_REGISTRYINDEX - idx - 1
		c := this.closure
		return c != nil && uvIdx < len(c.upvalues)
	}
	if idx == LUA_REGISTRYINDEX {
		return true
	}
	absIdx := this.absIndex(idx)
	return absIdx > 0 && absIdx <= this.top
}

func (this *LuaStack) get(idx int) luaValue {
	if idx < LUA_REGISTRYINDEX {
		uvIdx := LUA_REGISTRYINDEX - idx - 1
		c := this.closure
		if c == nil || uvIdx >= len(c.upvalues) {
			return nil
		}
		return *(c.upvalues[uvIdx].val)
	}
	if idx == LUA_REGISTRYINDEX {
		return this.state.registry
	}
	absIdx := this.absIndex(idx)
	if absIdx > 0 && absIdx <= this.top {
		return this.slots[absIdx-1]
	}
	return nil
}

func (this *LuaStack) set(idx int, val luaValue) {
	if idx < LUA_REGISTRYINDEX {
		uvIdx := LUA_REGISTRYINDEX - idx - 1
		c := this.closure
		if c != nil && uvIdx < len(c.upvalues) {
			*(c.upvalues[uvIdx].val) = val
		}
		return
	}
	if idx == LUA_REGISTRYINDEX {
		this.state.registry = val.(*LuaTable)
		return
	}
	absIdx := this.absIndex(idx)
	if absIdx > 0 && absIdx <= this.top {
		this.slots[absIdx-1] = val
		return
	}
	panic("stack set invalid idx")
}

func (this *LuaStack) reverse(from, to int) {
	slots := this.slots
	for from < to {
		slots[from], slots[to] = slots[to], slots[from]
		from++
		to--
	}
}
